#include <stdio.h>

#include <memory>

#include "xlcomm/net/tcp_server.h"
using namespace xlcomm;

static const char* g_file_name = "/home/xuel/code/xlcomm/C++StyleGuide.md";
static const int kBuffSize = 64;

// class FileTransferServer {
//  public:
//   FileTransferServer(net::EventLoop* loop, const net::InetAddress&
//   listen_addr)
//       : loop_(loop), server_(loop, listen_addr, "FileTransferServer") {
//     server_.set_connect_callback(
//         std::bind(&FileTransferServer::OnConnection, this, _1));
//     server_.set_disconnect_callback(
//         std::bind(&FileTransferServer::OnConnection, this, _1));
//     server_.set_write_complete_callback(
//         std::bind(&FileTransferServer::OnWriteComplete, this, _1));
//   }

//   void Start() { server_.Start(0); }

//  private:
//   void OnConnection(const net::TcpConnectionPtr& conn) {
//     if (conn->IsConnected()) {
//       FILE* file = ::fopen(g_file_name, "rb");
//       if (file != nullptr) {
//         char* buff[kBuffSize];
//         size_t n = ::fread(buff, 1, kBuffSize, file);
//         if (n > 0) {
//           conn->set_context(file);
//           conn->Send(buff, n);
//         } else {
//           ::fclose(file);
//           conn->Shutdown();
//         }
//       } else {
//         LOG_SYSERROR(fopen);
//         conn->Shutdown();
//       }
//     } else {
//       FILE* file = static_cast<FILE*>(conn->context());
//       if (file != nullptr) {
//         ::fclose(file);
//       }
//     }
//   }

//   void OnWriteComplete(const net::TcpConnectionPtr& conn) {
//     FILE* file = static_cast<FILE*>(conn->context());
//     char* buff[kBuffSize];
//     size_t n = ::fread(buff, 1, kBuffSize, file);
//     if (n > 0) {
//       conn->Send(buff, n);
//     } else {
//       ::fclose(file);
//       conn->set_context(nullptr);
//       conn->Shutdown();
//     }
//   }

//   net::EventLoop* loop_;
//   net::TcpServer server_;
// };

class FileTransferServer2 {
 public:
  using FilePtr = std::shared_ptr<FILE>;

  FileTransferServer2(net::EventLoop* loop, const net::InetAddress& listen_addr)
      : loop_(loop), server_(loop, listen_addr, "FileTransferServer2") {
    server_.set_connect_callback(
        std::bind(&FileTransferServer2::OnConnection, this, _1));
    server_.set_disconnect_callback(
        std::bind(&FileTransferServer2::OnConnection, this, _1));
    server_.set_write_complete_callback(
        std::bind(&FileTransferServer2::OnWriteComplete, this, _1));
    server_.Start(0);
  }
  void Start() { server_.Start(0); }

 private:
  void OnConnection(const net::TcpConnectionPtr& conn) {
    if (conn->IsConnected()) {
      FILE* file = ::fopen(g_file_name, "rb");
      FilePtr ctx(file, ::fclose);
      if (file != nullptr) {
        char* buff[kBuffSize];
        size_t n = ::fread(buff, 1, kBuffSize, file);
        if (n > 0) {
          conn->set_context(ctx);  // void set_context(boost::any context)
          conn->Send(buff, n);
        } else {
          conn->Shutdown();
        }
      } else {
        LOG_SYSERROR(fopen);
        conn->Shutdown();
      }
    } else {
    }
  }

  void OnWriteComplete(const net::TcpConnectionPtr& conn) {
    const FilePtr& file = boost::any_cast<const FilePtr&>(conn->context());
    char* buff[kBuffSize];
    size_t n = ::fread(buff, 1, kBuffSize, file.get());
    if (n > 0) {
      conn->Send(buff, n);
    } else {
      conn->Shutdown();
    }
  }

  net::EventLoop* loop_;
  net::TcpServer server_;
};

int main() {
  net::EventLoop loop;
  FileTransferServer2 server(&loop, net::InetAddress(20001));
  server.Start();
  loop.Loop();
  return 0;
}